import { isProxy, toRaw } from "vue"

//存档
export class Config {
    #original_config: any
    #user_config: any
    #changed_listeners: Array<() => void>

    constructor() {
        this.#original_config = {}
        this.#user_config = {}
        this.#changed_listeners = []
        this.#loadLocal()
        this.onChange(() => this.save())
    }

    #loadLocal() {
        const data = localStorage.getItem("user-config")
        if (data) {
            const config = JSON.parse(data)
            for (let key in config) {
                this.#user_config[key] = config[key]
            }
        }
    }

    save() {
        const data = JSON.stringify(this.#user_config)
        localStorage.setItem("user-config", data)
    }

    onChange(fn: () => void) {
        this.#changed_listeners.push(fn)
    }

    change() {
        this.#changed_listeners.forEach(fn => fn.apply(this))
    }

    get(key: string) {
        if (key.includes(".")) {
            const keys = key.split(".")
            let i = 0
            let data = Object.assign({}, this.#original_config, this.#user_config)
            while (i < keys.length) {
                data = data[keys[i]]
                ++i
            }
            return data
        }

        return this.#user_config[key] ?? this.#original_config[key]
    }

    fill(data: any) {
        for (let key in data) {
            this.set(key, data[key])
        }
    }

    set(key: string, value: any) {
        if (value instanceof Map) {
            value = mapToObject(value)
        }
        this.#user_config[key] = value
        this.change()
    }

    reset() {
        this.#user_config.clear()
        this.change()
    }

    get size() {
        return Math.max(this.#user_config.size, this.#original_config.size)
    }

    keys() {
        const userKeys = Object.keys(this.#user_config)
        const originKeys = Object.keys(this.#original_config)
        const keys = new Set(userKeys.concat(originKeys))
        return Array.from(keys)
    }

    *values() {
        const keys = this.keys()
        for (let key of keys) {
            yield this.get(key)
        }
    }

    static from(data: any) {
        const instance = new Config()
        for (let key in data) {
            instance.#original_config[key] = data[key]
        }
        return instance
    }
}

function mapToObject<K, V>(map: Map<K, V>) {
    const obj = Object.create(null)
    for (let [key, value] of map.entries()) {
        if (value instanceof Map) {
            value = mapToObject(value)
        }
        obj[key] = value
    }
    return obj
}
